package a.b.c

import a.b.c.databinding.ActivityMainBinding
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.provider.Settings
import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
import android.util.Base64
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.biometric.BiometricManager
import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG
import androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL
import androidx.biometric.BiometricPrompt
import androidx.biometric.BiometricPrompt.AUTHENTICATION_RESULT_TYPE_BIOMETRIC
import androidx.biometric.BiometricPrompt.AUTHENTICATION_RESULT_TYPE_DEVICE_CREDENTIAL
import androidx.biometric.BiometricPrompt.ERROR_CANCELED
import androidx.biometric.BiometricPrompt.ERROR_HW_NOT_PRESENT
import androidx.biometric.BiometricPrompt.ERROR_HW_UNAVAILABLE
import androidx.biometric.BiometricPrompt.ERROR_LOCKOUT
import androidx.biometric.BiometricPrompt.ERROR_LOCKOUT_PERMANENT
import androidx.biometric.BiometricPrompt.ERROR_NEGATIVE_BUTTON
import androidx.biometric.BiometricPrompt.ERROR_NO_BIOMETRICS
import androidx.biometric.BiometricPrompt.ERROR_NO_DEVICE_CREDENTIAL
import androidx.biometric.BiometricPrompt.ERROR_NO_SPACE
import androidx.biometric.BiometricPrompt.ERROR_SECURITY_UPDATE_REQUIRED
import androidx.biometric.BiometricPrompt.ERROR_TIMEOUT
import androidx.biometric.BiometricPrompt.ERROR_UNABLE_TO_PROCESS
import androidx.biometric.BiometricPrompt.ERROR_USER_CANCELED
import androidx.biometric.BiometricPrompt.ERROR_VENDOR
import androidx.core.content.ContextCompat
import java.nio.charset.Charset
import java.security.KeyStore
import java.util.Arrays
import java.util.concurrent.Executor
import javax.crypto.Cipher
import javax.crypto.KeyGenerator
import javax.crypto.SecretKey
import javax.crypto.spec.IvParameterSpec

/**
 * 生物识别：如指纹、面部、虹膜等
 * https://developer.android.google.cn/training/sign-in/biometric-auth?hl=zh-cn
 * https://blog.csdn.net/qq_44005305/article/details/136144963
 */
class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding
    private lateinit var executor: Executor
    private lateinit var biometricPrompt: BiometricPrompt
    private lateinit var promptInfo: BiometricPrompt.PromptInfo
    private var authenticationResultListener: ((BiometricPrompt.AuthenticationResult) -> Unit)? = null

    private val keystoreAlias = "myKeyAlias"
    private var encryptBase64: String? = null // 加密后的64字符串
    private var iv: ByteArray? = null // 解密的iv向量必须和加密时相同

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        executor = ContextCompat.getMainExecutor(this)
        biometricPrompt = BiometricPrompt(this, executor,
            object : BiometricPrompt.AuthenticationCallback() {
                override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
                    super.onAuthenticationError(errorCode, errString)
                    val msg = when (errorCode) {
                        ERROR_SECURITY_UPDATE_REQUIRED -> "发现一个或多个硬件传感器存在安全漏洞。在安全更新解决问题之前，受影响的传感器不可用"
                        ERROR_NO_DEVICE_CREDENTIAL -> "设备没有设置pin、模式或密码"
                        ERROR_NEGATIVE_BUTTON -> "用户点击了取消按钮"
                        ERROR_HW_NOT_PRESENT -> "设备没有所需的身份验证硬件"
                        ERROR_NO_BIOMETRICS -> "用户没有注册任何生物特征"
                        ERROR_USER_CANCELED -> "用户取消操作"
                        ERROR_LOCKOUT_PERMANENT -> "由于ERROR_LOCKOUT发生次数过多，操作被取消。在用户使用其设备凭据（即PIN、模式或密码）解锁之前，生物识别身份验证将被禁用"
                        ERROR_VENDOR -> "由于供应商特定的错误，操作失败"
                        ERROR_LOCKOUT -> "由于尝试过多，API被锁定，操作被取消。这发生在5次尝试失败后，并持续30秒"
                        ERROR_CANCELED -> "由于生物识别传感器不可用，操作被取消。当用户被切换、设备被锁定或其他挂起的操作阻止时，可能会发生这种情况"
                        ERROR_NO_SPACE -> "操作无法完成，因为剩余的设备存储空间不足"
                        ERROR_TIMEOUT -> "当前操作运行时间过长，已超时，通常为30秒"
                        ERROR_UNABLE_TO_PROCESS -> "传感器无法处理当前图像"
                        ERROR_HW_UNAVAILABLE -> "硬件不可用。稍后再试"
                        else -> errString
                    }
                    val errMsg = "验证错误: ($errorCode)$msg"
                    Toast.makeText(applicationContext, errMsg, Toast.LENGTH_SHORT).show()
                    showLog(errMsg)

                }

                override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
                    super.onAuthenticationSucceeded(result)
                    val type = when (result.authenticationType) {
                        AUTHENTICATION_RESULT_TYPE_DEVICE_CREDENTIAL -> "通过输入设备PIN、模式或密码进行身份验证"
                        AUTHENTICATION_RESULT_TYPE_BIOMETRIC -> "通过生物特征（如指纹或人脸）进行身份验证"
                        else -> "未知验证方式"
                    }
                    val msg = "验证成功：$type"
                    Toast.makeText(applicationContext, msg, Toast.LENGTH_SHORT).show()
                    showLog(msg)
                    authenticationResultListener?.invoke(result)
                }

                override fun onAuthenticationFailed() {
                    super.onAuthenticationFailed()
                    Toast.makeText(applicationContext, "验证失败：", Toast.LENGTH_SHORT).show()
                }
            })

        promptInfo = BiometricPrompt.PromptInfo.Builder()
            .setTitle("登录")
            .setSubtitle("验证指纹以继续操作")
            .setNegativeButtonText("取消")
            // setNegativeButtonText和setAllowedAuthenticators不能同时调用
            //.setAllowedAuthenticators(BIOMETRIC_STRONG or DEVICE_CREDENTIAL)
            //.setConfirmationRequired(true)
            .build()


        binding.btn01.setOnClickListener(::onClickBtn01)
        binding.btn02.setOnClickListener(::onClickBtn02)
        binding.btn03.setOnClickListener(::onClickBtn03)
        binding.btn04.setOnClickListener(::onClickBtn04)
    }

    private fun onClickBtn01(v: View) {
        biometricPrompt.authenticate(promptInfo)
    }

    private fun onClickBtn02(v: View) {
        val biometricManager = BiometricManager.from(this)
        val msg = when (biometricManager.canAuthenticate(BIOMETRIC_STRONG or DEVICE_CREDENTIAL)) {
            BiometricManager.BIOMETRIC_SUCCESS -> "可以使用生物识别"
            BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> "该设备无生物识别硬件"
            BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> "由于硬件不可用，用户无法进行身份验证。请稍后再试"
            BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> {
                Intent(Settings.ACTION_BIOMETRIC_ENROLL).run {
                    putExtra(Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED, BIOMETRIC_STRONG or DEVICE_CREDENTIAL)
                    startActivityForResult(this, 0)
                }
                "用户无法进行身份验证，因为没有注册生物特征或设备凭据"
            }

            else -> "无知错误"
        }
        showLog(msg)
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
    }

    private fun onClickBtn03(v: View) {
        authenticationResultListener = {
            val byteArray = it.cryptoObject?.cipher?.doFinal("hello".toByteArray(Charset.defaultCharset()))
            if (byteArray != null) {
                encryptBase64 = Base64.encodeToString(byteArray, Base64.DEFAULT)
                showLog("加密后的字节：${byteArray.contentToString()}")
                showLog("加密后的base64：$encryptBase64")
                // 需要使用加密时相同的iv向量才能解密
                iv = it.cryptoObject?.cipher?.iv
                showLog("存入加密时的iv向量=${iv.contentToString()}")
                Toast.makeText(this, "加密后的base64：$encryptBase64", Toast.LENGTH_SHORT).show()
            }
        }

        val cipher = getCipher()
        val secretKey = getSecretKey(keystoreAlias)

        // 加解密都是调用doFinal，具体取决于传入的模式：
        // ENCRYPT_MODE 加密
        // DECRYPT_MODE 解密
        cipher.init(Cipher.ENCRYPT_MODE, secretKey)

        // 调用生物识别授权使用密钥（在密钥密钥时指定了需要授权才能使用）
        biometricPrompt.authenticate(promptInfo, BiometricPrompt.CryptoObject(cipher))
    }

    private fun onClickBtn04(v: View) {
        authenticationResultListener = {
            val byteArray = it.cryptoObject?.cipher?.doFinal(Base64.decode(encryptBase64, Base64.DEFAULT))
            if (byteArray != null) {
                showLog("解密后的字节：${Arrays.toString(byteArray)}")
                showLog("解密后的字符串：${String(byteArray)}")
                Toast.makeText(this, "解密后的字符串：${String(byteArray)}", Toast.LENGTH_SHORT).show()
            }
        }

        val cipher = getCipher()
        val secretKey = getSecretKey(keystoreAlias)

        // 加解密都是调用doFinal，具体取决于传入的模式：
        // ENCRYPT_MODE 加密
        // DECRYPT_MODE 解密
        showLog("使用加密时的iv向量解密=${iv.contentToString()}")
        cipher.init(Cipher.DECRYPT_MODE, secretKey, IvParameterSpec(iv))
        // 调用生物识别授权使用密钥（在密钥密钥时指定了需要授权才能使用）
        biometricPrompt.authenticate(promptInfo, BiometricPrompt.CryptoObject(cipher))
    }

    /**
     * 获取密钥
     * @param keystoreAlias 密钥别名
     */
    private fun getSecretKey(keystoreAlias: String): SecretKey {
        // 初始化并加载Android的密钥库
        val keyStore = KeyStore.getInstance("AndroidKeyStore").apply {
            load(null)
        }
        // 获取密钥（通常无法直接为生成的密钥设置密码。原因是使用 Android KeyStore 时，
        // 密钥本身被安全地存储在设备的安全硬件或软件中，并且无法直接提取或导出。
        // 因此，密钥的保护依赖于系统级别的安全机制，而不是由应用程序显式地为其设置密码。）
        val key = keyStore.getKey(keystoreAlias, null)
        if (key != null) {
            return key as SecretKey
        }
        // 没有对应的密钥，创建密钥
        val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")
        val keyGenParameterSpec = KeyGenParameterSpec.Builder(keystoreAlias, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
            .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
            .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
            // 在使用密钥时要求用户进行身份验证（例如，使用密码、PIN、指纹等）
            .setUserAuthenticationRequired(true)
            // 设置注册（添加）新的生物识别后该密钥会失效
            .setInvalidatedByBiometricEnrollment(true)
            .also {
                // 设置用户成功通过身份验证后授权使用此密钥的持续时间（秒）
                // 如果密钥需要用户身份验证才能使用，则此设置有效（请参阅 setUserAuthenticationRequired(boolean)）。
                // ps(可以使用key.getEncoded()方法存起来，在有效期内就不必每次需要用户验证，有效期内直接使用SecretKeySpec(key: ByteArray, algorithm: String)恢复密钥)
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                    // 设置30秒有效期
                    it.setUserAuthenticationParameters(30, KeyProperties.AUTH_BIOMETRIC_STRONG)
                } else {
                    // 设置30秒有效期
                    it.setUserAuthenticationValidityDurationSeconds(30)
                }
            }
            .build()
        keyGenerator.init(keyGenParameterSpec)
        return keyGenerator.generateKey()
    }

    private fun getCipher(): Cipher {
        return Cipher.getInstance(
            KeyProperties.KEY_ALGORITHM_AES + "/"
                    + KeyProperties.BLOCK_MODE_CBC + "/"
                    + KeyProperties.ENCRYPTION_PADDING_PKCS7
        )
    }

    private fun showLog(msg: String) {
        Log.i("duxl.log", "$msg")
    }
}